/**
 *  @file DLinkedList.c
 */
#if defined(linux)
/*** required on Linux or gcc can't recognize 'wchar_t' ***/
#   include <stdlib.h>
#endif
#include "../commontypes.h"
#include "DLinkedList.h"


/**
 *  Initialize a d-linked struct
 */
void DL_InitDlList(dllist_t *pList)
{
    DL_BreakupLink(pList);
}


/**
 *  Set both "previous" and "next" pointers to NULL 
 */
void DL_BreakupLink(dllist_t *pList)
{
    pList->pNext = pList->pPrev = NULL;
}

/**
 *  Add itself to list head.
 *
 *  @param[in,out] pHead -- head pointer will be updated.
 *  @param[in,out] pTail -- tail pointer will be updated if there is no entry
 *                          in the list originally.
 *                          Pass NULL if this is a link list without "tail"
 *                          pointer.
 */
void DL_AddEntryToHead(dllist_t *pList, dllist_t ** pHead, dllist_t ** pTail)
{
    // in case we want to update the tail
    if (pTail && *pTail == NULL) {
      *pTail = pList;
    }

    if (*pHead == NULL) {
      *pHead = pList;
    }
    else {
       (*pHead)->pPrev = pList;
       pList->pNext = *pHead;
       pList->pPrev = NULL;
       *pHead = pList;
    }
}

/**
 *  Add itself to list tail.
 *
 *  @param[in,out] pHead -- head pointer will be updated if there is no entry
 *                          in the list originally.
 *  @param[in,out] pTail -- tail pointer will be updated.
 */
void DL_AddEntryToTail(dllist_t *pList, dllist_t ** pHead, dllist_t ** pTail)
{
    if (*pHead == NULL)
      *pHead = pList;

    if (*pTail == NULL) {
      *pTail = pList;  
    }
    else {
       (*pTail)->pNext = pList;
       pList->pPrev = *pTail;
       pList->pNext = NULL;
       *pTail = pList;
    }
}

/**
 *  Insert itself between the 2 entries
 */
void DL_InsertEntry(dllist_t *pList, dllist_t * pPrevEntry, dllist_t * pNextEntry)
{
    pList->pPrev = pPrevEntry;
    pList->pNext = pNextEntry;
    pPrevEntry->pNext = pList;
    pNextEntry->pPrev = pList;
}

/**
 *  Get the next list entry
 */
dllist_t * DL_GetNextEntry(dllist_t *pList)
{
    return pList->pNext;    
}

/**
 *  Get the previous list entry
 */
dllist_t * DL_GetPrevEntry(dllist_t *pList)
{
    return pList->pPrev;
}

/**
 *  Unlink itself from the list.
 *
 *  @note memory is not deleted, it has to be freed from the outside world.
 *
 *  @param[in,out] pHead -- may be updated
 *  @param[in,out] pTail -- may be updated
 *                          Pass NULL if this is a link list without "tail"
 *                          pointer.
 */
dllist_t * DL_DeleteEntry(dllist_t *pList, dllist_t ** pHead, dllist_t ** pTail)
{
    dllist_t * pPrev, * pNext;

    pPrev = pList->pPrev;
    pNext = pList->pNext;

    if (pPrev != NULL) {
       pPrev->pNext = pList->pNext;
    }
    else {
       *pHead = pList->pNext;
    }

    if (pNext != NULL) {
       pNext->pPrev = pList->pPrev;
    }     
    else {
       // in case we want to update the tail
       if (pTail)
          *pTail = pList->pPrev;
    }  

    // clear at last
    DL_BreakupLink(pList);


	return pList;
}

/**
 *  Get original pointer
 *
 *  @param [in] pOffset -- offset of the pList in the original structure
 *
 *  @return pointer to original structure
 */
void * DL_GetOrgPtr(void *pList, void *pOffset)
{
   return pList ? (void*)((ptr_t)pList - (ptr_t)pOffset) : NULL;
}
